/*****************************************************************************
 *  FFTerm - Simple & Platform independent, Thread-Safe Terminal/Console     *
 *  Copyright (C) 2009  James Walmsley (james@worm.me.uk)                    *
 *                                                                           *
 *  This program is free software: you can redistribute it and/or modify     *
 *  it under the terms of the GNU General Public License as published by     *
 *  the Free Software Foundation, either version 3 of the License, or        *
 *  (at your option) any later version.                                      *
 *                                                                           *
 *  This program is distributed in the hope that it will be useful,          *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
 *  GNU General Public License for more details.                             *
 *                                                                           *
 *  You should have received a copy of the GNU General Public License        *
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.    *
 *                                                                           *
 *  IMPORTANT NOTICE:                                                        *
 *  =================                                                        *
 *  Alternative Licensing is available directly from the Copyright holder,   *
 *  (James Walmsley). For more information consult LICENSING.TXT to obtain   *
 *  a Commercial license.                                                    *
 *                                                                           *
 *  See RESTRICTIONS.TXT for extra restrictions on the use of FFTerm.        *
 *                                                                           *
 *  Removing the above notice is illegal and will invalidate this license.   *
 *****************************************************************************
 *  See http://worm.me.uk/ffterm for more information.                       *
 *  Or  http://ffterm.googlecode.com/ for latest releases and the wiki.      *
 *****************************************************************************/

/**
 *	@file		FFTerm.c
 *	@author		James Walmsley
 *	@ingroup	FFTERM
 *
 *	@defgroup	FFTERM	FFTerm
 *	@brief		A platform independent console/terminal program.
 *
 *	Provides a simple and extendable terminals running over multiple, thread-safe
 *	independent consoles.
 *
 **/

#ifndef _FFTERM_H_
#define _FFTERM_H_

#include "ffterm-types.h"
#include "ffterm-error.h"
#include "ffterm-platform.h"
#include "ffterm-config.h"

#include <stdio.h>

#define FFT_VERSION_NUMBER		"0.9.1"
#define	FFT_REVISION_NUMBER		"svn-r40"
#define FFT_RELEASE_NAME		"Graceful Owl"

typedef FF_T_SINT32 (*FFT_THREADROUTINE)		(void *pParam);	// Mult-threaded background jobs.

typedef void		(*FFT_ONCREATE)			();
typedef void		(*FFT_ONEXIT)			();
typedef int			(*FFT_FGETC)			(FILE *f);
typedef FF_T_UINT64	(*FFT_GETTICKS)			();
typedef	FF_T_UINT64	(*FFT_GETTICKRATE)		();
typedef FF_T_UINT32	(*FFT_GETCONSOLEWIDTH)	();
typedef FF_T_UINT32	(*FFT_GETCONSOLEHEIGHT)	();
typedef void		(*FFT_SETCONSOLECOLOUR)	(FF_T_UINT16 usColour);
typedef void		(*FFT_CREATEBGTHREAD)	(FFT_THREADROUTINE pfnRoutine, void *pParam);
typedef void		(*FFT_TABULATEFS)		();

typedef struct {
	const FF_T_UINT8	ucReturnChar;
	const FF_T_UINT8	ucBackspaceChar;
	const FF_T_UINT8	ucTabChar;
	const FF_T_UINT8	ucEscapeChar;
	const FF_T_INT8		*szpCrLf;
	const FF_T_UINT32	ulDefaultMode;
} FFT_CUSTOMPARAMS;

typedef struct {
	const FFT_CUSTOMPARAMS		*pCustomParams;
	FFT_ONCREATE				pfnOnCreate;
	FFT_ONEXIT					pfnOnExit;
	FFT_FGETC					pfnFgetC;
	FFT_GETTICKS				pfnGetTicks;
	FFT_GETTICKRATE				pfnGetTickRate;
	FFT_GETCONSOLEWIDTH			pfnGetConsoleWidth;
	FFT_GETCONSOLEHEIGHT		pfnGetConsoleHeight;
	FFT_SETCONSOLECOLOUR		pfnSetConsoleColour;
	FFT_CREATEBGTHREAD			pfnCreateBgThread;
	FFT_TABULATEFS				pfnTabulateFs;
} FFT_PLATFORMSPEC;

#define	FFT_ENABLE_ECHO_INPUT	0x0004
#define FFT_ENABLE_LINE_INPUT	0x0002
#define FFT_ENABLE_WINDOWS		0x8000

#define FFT_CONSOLE_WIDTH		80
#define FFT_CONSOLE_HEIGHT		25

#define FFT_MODE_DEFAULT		FFT_ENABLE_ECHO_INPUT
#define WINDOWS

#define FFT_MAX_CMD_NAME		10
#define FFT_MAX_CMD_PROMPT		20

#define FFT_MAX_CMD_LINE_INPUT	2048	// 2Kb Per Command Line
//#define FFT_MAX_CMD_ARG_LENGTH	128
#define FFT_MAX_CMD_LINE_ARGS	256		// 1Kb of Argument pointers
#define FFT_MAX_HISTORY			50

#define FFT_MAX_BG_JOBS			10		// Maximum number of concurrent jobs to be executed.

#define FFT_MAX_ESCAPE_SEQUENCE	10


#ifdef __WIN32__
#define FFT_RETURN			0x0D
#define FFT_BACKSPACE		0x08
#define FFT_TAB				0x09
#define	FFT_ESCAPE			0x1B
#define FFT_CRLF			"\n"
#else

#define FFT_BACKSPACE			127 //0x08 //'\b'	//0x08
//#define FFT_TAB					'\t'	//0x09
#define FFT_TAB 					0x09
#define FFT_ESCAPE				0x1B
#define FFT_RETURN 				'\n'
#define FFT_CRLF				"\r\n"
#endif

#define FFT_KILL_CONSOLE		-666	///< Special return value from any Command to kill the console.
#define FFT_COMMAND_DESCRIPTION	-1001	///< Special Error Code for getting a Command Description.

typedef int (*FFT_FN_COMMAND)	 (int argc, char **argv);				// Deliberately left as platform types.
typedef int (*FFT_FN_COMMAND_EX) (int argc, char **argv, void *pParam);	// Extended Command function.

typedef struct {
	void *pParam;		// This can be allocated any platform specific object!
} FFT_TABULATE_CONTEXT;

typedef FF_T_SINT32 (*FFT_FN_TABULATE_FIRST) 	(FF_T_INT8 *szWildPath, FF_T_INT8 *szOutputName, FFT_TABULATE_CONTEXT *pContext);
typedef FF_T_SINT32 (*FFT_FN_TABULATE_NEXT)		(FF_T_INT8 *szOutputName, FFT_TABULATE_CONTEXT *pContext);
typedef FF_T_SINT32	(*FFT_FN_TABULATE_CLOSE)	(FFT_TABULATE_CONTEXT *pContext);


typedef struct {
	FFT_FN_TABULATE_FIRST	fnTabFirst;
	FFT_FN_TABULATE_NEXT	fnTabNext;
	FFT_FN_TABULATE_CLOSE	fnTabClose;
} FFT_TABULATE;

typedef struct {
	const FF_T_INT8 * const strErrorString;
    const FF_T_SINT32 iErrorCode;
} FFT_ERR_TABLE;

typedef struct _FFT_COMMAND {
	FF_T_INT8			cmdName[FFT_MAX_CMD_NAME + 1];
	FFT_FN_COMMAND		fnCmd;
	FFT_FN_COMMAND_EX	fnCmdEx;
	void				*CmdExParam;
	const FFT_ERR_TABLE	*ErrTable;
	struct _FFT_COMMAND	*pNextCmd;		///< Pointer to the next command in the linked list.
} FFT_COMMAND;

typedef struct {
			 FF_T_INT8			strCmdPrompt[FFT_MAX_CMD_PROMPT];
			 //FF_T_INT8		strCmdLine[FFT_MAX_CMD_LINE_INPUT];
			 //FF_T_INT8		*pArgs[FFT_MAX_CMD_LINE_ARGS];
			 FF_T_INT8			strCmdHistory[FFT_MAX_HISTORY][FFT_MAX_CMD_LINE_INPUT];
			 FF_T_UINT32		ulMode;
	volatile FF_T_BOOL			bKill;		///< Volatile because it can be set in a multi-threaded environment. (Or the exit command!);
			 FFT_COMMAND		*pCommands;
			 FILE				*pStdIn;
			 FILE				*pStdOut;
//			 FF_T_SINT32		cCRLF[2];		///< Carriage Return Line Feed Char's.
			 FFT_TABULATE		*pTabulate;		///< Allow hooking of tab auto-completion!
			 FFT_PLATFORMSPEC	*pSpec;	
} FFT_CONSOLE;


extern FF_T_SINT32	 FFTerm_HookDefaultCommands	(FFT_CONSOLE *pConsole);
FFT_CONSOLE			*FFTerm_CreateConsole		(FF_T_INT8 *pa_strCmdPrompt, FILE *pa_pStdIn, FILE * pa_pStdOut, FF_T_SINT32 *pError);
FF_T_SINT32			 FFTerm_RegisterPlatformSpecification	(FFT_CONSOLE *pConsole, FFT_PLATFORMSPEC *pSpec);
FF_T_SINT32			 FFTerm_StartConsole		(FFT_CONSOLE *pConsole);
FF_T_SINT32			 FFTerm_KillConsole			(FFT_CONSOLE *pConsole);
FF_T_SINT32			 FFTerm_AddCmd				(FFT_CONSOLE *pConsole, const FF_T_INT8 *pa_cmdName, FFT_FN_COMMAND pa_fnCmd, const FFT_ERR_TABLE *pa_ErrTable);
FF_T_SINT32			 FFTerm_AddExCmd			(FFT_CONSOLE *pConsole, const FF_T_INT8 *pa_cmdName, FFT_FN_COMMAND_EX pa_fnCmd, const FFT_ERR_TABLE *pa_ErrTable, void *pParam);
FF_T_SINT32			 FFTerm_RemoveCmd			(FFT_CONSOLE *pConsole, const FF_T_INT8 *pa_cmdName);
FF_T_SINT32			 FFTerm_SetConsoleMode		(FFT_CONSOLE *pConsole, FF_T_UINT32 Mode);
FF_T_SINT32			 FFTerm_GetConsoleMode		(FFT_CONSOLE *pConsole, FF_T_UINT32 *Mode);
FF_T_UINT32			 FFTerm_GetConsoleWidth		(FFT_CONSOLE *pConsole);
FF_T_UINT32			 FFTerm_GetConsoleHeight	(FFT_CONSOLE *pConsole);
void				 FFTerm_SetConsoleColour	(FFT_CONSOLE *pConsole, FF_T_UINT16 ulColour);

void				 FFTerm_SetCRLFSequence		(FFT_CONSOLE *pConsole, FF_T_INT8 cCR, FF_T_INT8 cLF);

const FF_T_INT8		*FFTerm_LookupErrMessage	(const FFT_ERR_TABLE *pa_pErrTable, FF_T_SINT32 iErrorCode);
FF_T_SINT32			FFTerm_ExecCommand			(FFT_CONSOLE *pConsole, FF_T_INT8 *strCommand);
FF_T_SINT32			FFTerm_ExecCommandInBG		(FFT_CONSOLE *pConsole, FF_T_INT8 *strCommand);

typedef struct {
	int		tab_interation;
	char	lastToken[FFT_MAX_CMD_NAME];
	char	origToken[FFT_MAX_CMD_NAME];
	int		charsAdded;
	int		charsToRemove;
	int		iOrginalTokenPos;
} FFT_TAB_CONTEXT;

// Tabulation Stuff
FF_T_SINT32 FFTerm_TabulateAction(FFT_CONSOLE *pConsole, FF_T_INT8 *strCommandLine, FFT_TAB_CONTEXT *pTabCtx);


typedef struct {
	FF_T_INT8	Sequence[FFT_MAX_ESCAPE_SEQUENCE];
} FFT_ESCAPE_SEQUENCE;

typedef struct {
	FFT_ESCAPE_SEQUENCE Up;
	FFT_ESCAPE_SEQUENCE Down;
	FFT_ESCAPE_SEQUENCE Left;
	FFT_ESCAPE_SEQUENCE Right;
} FFT_ESCAPE_SEQUENCES;

// Getopt stuff

#define FFT_no_argument			0
#define FFT_required_argument 	1
#define FFT_optional_argument	2

struct FFT_option {
	const char *name;
	int has_arg;
	int *flag;
	int val;
};

typedef struct {
	int optind;
	int nextchar;
	int optopt;
	const char *optarg;
} FFT_GETOPT_CONTEXT;

typedef struct {
	int 			optind;
	int 			nextchar;
	int 			optopt;
	const char 		*optarg;
	struct option 	*longopts;		///< Pointer to an options structure.
	int				*longindex;		///< Stores the index value through a pointer (as per GNU spec).
	int				longindex_val;	///< Stores the index value here directly, instead.
} FFT_GETOPTLONG_CONTEXT;

int FFTerm_getopt			(int argc, const char **argv, const char *optstring, FFT_GETOPT_CONTEXT *ctx);
//int FFTerm_getopt_long		(int argc, const char **argv, const char *optstring, FFT_GETOPTLONG_CONTEXT *ctx);
//int FFTerm_getopt_long_only	(int argc, const char **argv, const char *optstring, FFT_GETOPTLONG_CONTEXT *ctx);
const char *FFTerm_getarg	(int argc, const char **argv, int argindex, FFT_GETOPT_CONTEXT *ctx);

typedef struct {
	FF_T_INT8	 strCommandLine[FFT_MAX_CMD_LINE_INPUT];
	FF_T_INT8	*pArgs[FFT_MAX_CMD_LINE_ARGS];
	FF_T_SINT32  Argc;
	FF_T_SINT32	 retVal;
	FFT_CONSOLE	*pConsole;
} FFT_THREADPARAMS;

typedef struct {
	FF_T_UINT16		usPid;	///< Job Number (Pid!)
	FF_T_BOOL		bInUse;
	FF_T_BOOL		bExecuted;
	FFT_COMMAND		*pCommand;
	FFT_THREADPARAMS threadParams;
} FFT_BGJOBS;

FFT_BGJOBS *FFTerm_GetJobs();



#endif
